package com.lyx.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.lyx.common.constant.RedisContant;
import com.lyx.common.exception.BusinessException;
import com.lyx.common.exception.ExceptionEnum;
import com.lyx.common.to.UserTo;
import com.lyx.common.utils.JwtUtils;
import com.lyx.user.dto.UserDto;
import com.lyx.user.dto.WxLoginDto;
import com.lyx.user.entity.User;
import com.lyx.user.mapper.UserMapper;
import com.lyx.user.service.UserService;
import com.lyx.user.utils.ConstantWxUtils;
import com.lyx.user.utils.HttpClientUtils;
import com.lyx.user.vo.UserVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 14667
 * @date 2022/7/18 20:57
 */
@Component
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    StringRedisTemplate redisTemplate;

    @Value("${user.defaultprefx}")
    String usernamePrefx;

    @Value("${user.defaultAvatar}")
    String userAvatar;

    @Override
    public WxLoginDto login(String phone, String code) {
        System.out.println(usernamePrefx);
        //接口防刷
        if(StringUtils.isEmpty(phone)||StringUtils.isEmpty(code)){
            throw new BusinessException(ExceptionEnum.MISSING_PHONE_OR_CODE);
        }
        //查看该号码是否存在
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("phone", phone));
        String code1 = redisTemplate.opsForValue().get(phone);
        if(StringUtils.isEmpty(code1)){
            throw new BusinessException(ExceptionEnum.CODE_INVALID);
        }else{
            if(!code.equals(code1)){
                throw new BusinessException(ExceptionEnum.CODE_ERROR);
            }
        }
        WxLoginDto wxLoginDto = new WxLoginDto();

        if(user!=null){
            String token = JwtUtils.getJwtToken(user.getId().toString(), user.getUserName());
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(user,userDto);
            wxLoginDto.setUserDto(userDto);
            wxLoginDto.setUserExistStatus(true);
            wxLoginDto.setToken(token);

            //登录
            redisTemplate.opsForValue().set(String.format(RedisContant.TOKEN,user.getId()),token);
            redisTemplate.delete(phone);
            return wxLoginDto;

        }else{
            //注册
            user = new User();
            user.setUserName(usernamePrefx+phone.substring(phone.length()-4,phone.length()));
            user.setAvatar(userAvatar);
            user.setPhone(phone);
            int insert = baseMapper.insert(user);
            User user1 = baseMapper.selectOne(new QueryWrapper<User>().eq("phone", phone));
            if(insert>0){
                String token = JwtUtils.getJwtToken(user1.getId().toString(), user1.getUserName());
                UserDto userDto = new UserDto();
                BeanUtils.copyProperties(user1,userDto);
                wxLoginDto.setUserDto(userDto);
                wxLoginDto.setUserExistStatus(true);
                wxLoginDto.setToken(token);
                redisTemplate.opsForValue().set(String.format(RedisContant.TOKEN,user1.getId()),token);
                return wxLoginDto;
            }else{
                throw new BusinessException(ExceptionEnum.PHONE_LOGIN_FAIL);
            }
        }
    }

    @Override
    public User getUserByOpenId(String openid) {
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("open_id", openid));
        return user;
    }

    @Override
    public void logout(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        String id = JwtUtils.getMemberIdByJwtToken(request);
        Integer userId = null;
        if(!id.equals("")){
            userId = Integer.parseInt(id);
            String s = redisTemplate.opsForValue().get(String.format(RedisContant.TOKEN, userId));
            if(!jwtToken.equals(s)){
                return;
            }
        }else{
            return;
        }
        //清除redis中的token
        String sId = String.valueOf(userId);
        redisTemplate.delete(String.format(RedisContant.TOKEN, sId));
    }

    @Override
    public WxLoginDto wxlogin(String code) {
        try {
            //1 获取code值，临时票据，类似于验证码
            //2 拿着code请求 微信固定的地址，得到两个值 accsess_token 和 openid
            String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                    "?appid=%s" +
                    "&secret=%s" +
                    "&code=%s" +
                    "&grant_type=authorization_code";
            //拼接三个参数 ：id  秘钥 和 code值
            String accessTokenUrl = String.format(
                    baseAccessTokenUrl,
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    ConstantWxUtils.WX_OPEN_APP_SECRET,
                    code
            );
            //请求这个拼接好的地址，得到返回两个值 accsess_token 和 openid
            //使用httpclient发送请求，得到返回结果
            String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);

            //从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
            //把accessTokenInfo字符串转换map集合，根据map里面key获取对应值
            //使用json转换工具 Gson
            Gson gson = new Gson();
            HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
            String access_token = (String)mapAccessToken.get("access_token");
            String openid = (String)mapAccessToken.get("openid");

            //把扫描人信息添加数据库里面，相当于注册功能了
            //判断数据表里面是否存在相同微信信息，根据openid判断
            User user = this.getUserByOpenId(openid);
            if(user == null) {//memeber是空，表没有相同微信数据，进行添加

                //3 拿着得到accsess_token 和 openid，再去请求微信提供固定的地址，获取到扫描人信息
                //访问微信的资源服务器，获取用户信息
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                //拼接两个参数
                String userInfoUrl = String.format(
                        baseUserInfoUrl,
                        access_token,
                        openid
                );
                //发送请求
                String userInfo = HttpClientUtils.get(userInfoUrl);
                System.out.println(userInfo);
                //获取返回userinfo字符串扫描人信息
                HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
                String nickname = (String)userInfoMap.get("nickname");
                String headimgurl = (String)userInfoMap.get("headimgurl");
                System.out.println(nickname);
                System.out.println(headimgurl);
                UserDto userDto = new UserDto();
                userDto.setUserName(nickname);
                userDto.setOpenId(openid);
                userDto.setAvatar(headimgurl);
                userDto.setPhone(null);
                userDto.setId(null);
                WxLoginDto wxLoginDto = new WxLoginDto();
                wxLoginDto.setUserDto(userDto);
                wxLoginDto.setUserExistStatus(false);
                wxLoginDto.setToken(null);
                return wxLoginDto;
            }
            String jwtToken = JwtUtils.getJwtToken(user.getId().toString(), user.getUserName());
            Integer id = user.getId();
            String sId = String.valueOf(id);
            redisTemplate.opsForValue().set(String.format(RedisContant.TOKEN,sId),jwtToken);
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(user,userDto);
            WxLoginDto wxLoginDto = new WxLoginDto();
            wxLoginDto.setUserExistStatus(true);
            wxLoginDto.setUserDto(userDto);
            wxLoginDto.setToken(jwtToken);
            return wxLoginDto;
        }catch(Exception e) {
            throw new BusinessException(ExceptionEnum.WX_LOGIN_FAIL);
        }
    }

    @Override
    public WxLoginDto add(UserVo userVo) {
        String username = userVo.getUserName();
        String phone = userVo.getPhone();
        String avatar = userVo.getAvatar();
        String openId = userVo.getOpenId();
        String code = userVo.getCode();

        if(StringUtils.isEmpty(openId)){
            throw new BusinessException(ExceptionEnum.ILLEGAL_LOGIN);
        }

        if(StringUtils.isEmpty(phone)||StringUtils.isEmpty(code)){
            throw new BusinessException(ExceptionEnum.MISSING_PHONE_OR_CODE);
        }
        String code1 = redisTemplate.opsForValue().get(phone);
        if(StringUtils.isEmpty(code1)){
            throw new BusinessException(ExceptionEnum.CODE_INVALID);
        }else if(!code.equals(code1)){
            throw new BusinessException(ExceptionEnum.CODE_ERROR);
        }
        User one = baseMapper.selectOne(new QueryWrapper<User>().eq("phone", phone));
        if(one!=null){
            if(Objects.nonNull(one.getOpenId())){
                throw new BusinessException(ExceptionEnum.PHONE_REGIST);
            }
            one.setOpenId(openId);
            one.setUserName(username);
            one.setAvatar(avatar);
            int insert = baseMapper.updateById(one);
            if (insert<=0){
                throw new BusinessException(ExceptionEnum.PHONE_BIND_FAIL);
            }
        }else{
            one = new User();
            BeanUtils.copyProperties(userVo,one);
            int insert = baseMapper.insert(one);
            if (insert<=0){
                throw new BusinessException(ExceptionEnum.PHONE_BIND_FAIL);
            }
        }
        User user1 = baseMapper.selectOne(new QueryWrapper<User>().eq("open_id", openId));
        redisTemplate.delete(phone);
        String token = JwtUtils.getJwtToken(one.getId().toString(), one.getUserName());
        redisTemplate.opsForValue().set(String.format(RedisContant.TOKEN,user1.getId()),token);
        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(user1,userDto);
        WxLoginDto wxLoginDto = new WxLoginDto();
        wxLoginDto.setToken(token);
        wxLoginDto.setUserDto(userDto);
        wxLoginDto.setUserExistStatus(true);
        return wxLoginDto;


    }

    @Override
    public User getUserInfo(HttpServletRequest request) {
        //这里不在需要校验了  因为在网关层已经统一进行了校验，能到这id肯定是正确的了
        String id = request.getHeader(RedisContant.USER_ID);
        Integer userId = Integer.parseInt(id);
        User user = baseMapper.selectOne(new QueryWrapper<User>().eq("id", userId));
        return user;
    }

    @Override
    public List<UserTo> getUsers(List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return new ArrayList<UserTo>();
        }
        List<User> users =  baseMapper.getUsersByIds(ids);
        if(CollectionUtils.isEmpty(users)){
            return new ArrayList<UserTo>();
        }
         List<UserTo> collect = users.stream().map(user -> {
            UserTo userTo = new UserTo();
            BeanUtils.copyProperties(user, userTo);
            return userTo;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public User getUserById(Integer userId) {
        User user = baseMapper.selectById(userId);
        if(Objects.isNull(user)){
            throw new BusinessException(ExceptionEnum.NOT_EXIST.getCode(),String.format(ExceptionEnum.NOT_EXIST.getMsg(),"用户"));
        }
        return user;
    }
}
